package com.qst.yunpan.service.impl;

import com.baidubce.services.doc.DocClient;
import com.qst.yunpan.dao.FileMapper;
import com.qst.yunpan.dao.OfficeMapper;
import com.qst.yunpan.dao.UserMapper;
import com.qst.yunpan.entity.*;
import com.qst.yunpan.service.FileService;
import com.qst.yunpan.utils.FileUtils;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.io.File;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@Service
public class FileServiceImpl implements FileService {
    //文件相对前缀
    public static final String PREFIX = "WEB-INF" + File.separator + "file" +File.separator; //File.separator 的作用相当于 ' \ ',因此File.separator的作用就是保证在任何系统都可以正确表示斜线
    //新用户注册初始化默认四个文件夹
    public static final String[] DEFAULT_DIRECTORY = {"void","music","source","image"};

    @Autowired
    private UserMapper userMapperDao;
    @Autowired
    private FileMapper fileMapperDao;
    @Autowired
    private OfficeMapper officeMapper;


    @Override
    public void addNewNameSpace( HttpServletRequest request,String namespace) {
        String fileName = getRootPath(request);
        File file = new File(fileName,namespace);
        file.mkdir();
        for(String newFileName : DEFAULT_DIRECTORY){
            File newFile = new File(file,newFileName);
            newFile.mkdir();
        }
    }

    //获取文件路径
    @Override
    public String getFilePath(HttpServletRequest request, String fileName) {

        if (fileName == null || fileName.equals("\\")) {
            fileName = "";
        }
        fileName = fileName.replace("\\","//");

        User user = (User) request.getSession().getAttribute("user");
        String username = user.getUsername();
        return getRootPath(request) + username + File.separator + fileName;
    }

    //根据用户名获取文件路径
    @Override
    public String getFilePath(HttpServletRequest request,String fileName,String username) {
        if(username == null){
            return getFilePath(request,fileName);
        }
        if(fileName == null ){
            fileName = "";
        }
        return getRootPath(request) + username + File.separator + fileName;
    }

    @Override
    public List<FileCustom> listFile(String realPath) {
        //对文件操作  需要new出一个文件，代表指向该文件内存地址
        File[] files = new File(realPath).listFiles();
        System.out.println("+++++++++++++++++++++++++");
        List<FileCustom> lists = new ArrayList<FileCustom>();
        if (files != null) {
            for (File file : files) {
                if(!file.getName().equals(User.RECYCLE)){
                    FileCustom custom = new FileCustom();
                    custom.setFileName(file.getName());
                    custom.setLastTime(FileUtils.formatTime(file.lastModified()));
                    custom.setCurrentPath(realPath);
                    if (file.isDirectory()) {//如果该文件是目录  则不计算大小
                        custom.setFileSize("-");
                    } else {
                        custom.setFileSize(FileUtils.getDataSize(file.length()));
                    }
                    custom.setFileType(FileUtils.getFileType(file));
                    lists.add(custom);
                    System.out.println(custom+"----------------");
                }

            }
        }
        return lists;


    }

    @Override
    public void uploadFilePath(HttpServletRequest request, MultipartFile[] files, String currentPath) throws Exception {
        for (MultipartFile file : files) {
            String fileName = file.getOriginalFilename();
            String filePath = getFilePath(request, currentPath);
            // 公司的域名+当前系统时间的毫秒值+随机几位数.jpg
//            assert fileName != null;
            File distFile = new File(filePath, fileName);
            if (!distFile.exists()) {
                // 上传文件
                file.transferTo(distFile);
                System.out.print("----------" + FileUtils.getFileType(distFile));
                if ("office".equals(FileUtils.getFileType(distFile))) {
                    String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
                    DocClient docClient = FileUtils.getDocClient();
                    String documentId = docClient.createDocument(distFile, fileName, suffix).getDocumentId();
                    System.out.println(documentId);
                    System.out.println(FileUtils.MD5(distFile));
                    Office office = new Office();
                    office.setOfficeid(documentId);
                    office.setOfficemd5(FileUtils.MD5(distFile));
                    officeMapper.insert(office);
                    System.out.println(documentId);
                }
            }
        }
        reSize(request); // 每个用户分配了10G的容量，每上传一次，要计算文件的大小，设置整体用的容量是多少
    }

    public void reSize(HttpServletRequest request) {
        User user = (User) request.getSession().getAttribute("user");
        String username = user.getUsername();
        try {
            user.setCountsize(countFileSize(request));
            UserExample example=new UserExample();
            example.createCriteria().andUsernameEqualTo(username);
            userMapperDao.updateByExample(user,example);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public String countFileSize(HttpServletRequest request) {
        long countFileSize = countFileSize(new File(getFilePath(request, null)));
        return FileUtils.getDataSize(countFileSize);
    }

    private long countFileSize(File srcFile) {
        File[] listFiles = srcFile.listFiles();
        if (listFiles == null) {
            return 0;
        }
        long count = 0;
        for (File file : listFiles) {
            if(!file.getName().equals(User.RECYCLE)){
                if (file.isDirectory()) {
                    count += countFileSize(file);
                } else {
                    count += file.length();
                }
            }

        }
        return count;
    }

    public String getRootPath(HttpServletRequest request){
        String rootPath = request.getSession().getServletContext().getRealPath("/") + PREFIX;
        return rootPath;
    }

    @Override
    public File downPackage(HttpServletRequest request, String currentPath, String[] fileNames, String username) throws Exception {
        File downloadFile = null;
        if (currentPath == null) {
            currentPath = "";
        }
        //单文件length为1
        if (fileNames.length == 1) {
            downloadFile = new File(getFilePath(request, currentPath, username), fileNames[0]);
            if (downloadFile.isFile()) {
                return downloadFile;
            }
        }
        String[] sourcePath = new String[fileNames.length];
        for (int i = 0; i < fileNames.length; i++) {
            sourcePath[i] = getFilePath(request, currentPath, username) + File.separator + fileNames[i];
        }
        String packageZipName = packageZip(sourcePath);
        downloadFile = new File(packageZipName);
        return downloadFile;
    }

    // 压缩文件
    private String packageZip(String[] sourcePath) throws Exception {
        String zipName = sourcePath[0] + (sourcePath.length == 1 ? "" : "等" + sourcePath.length + "个文件") + ".zip";
        ZipOutputStream zos = null;
        try {
            zos = new ZipOutputStream(new FileOutputStream(zipName));
            for (String string : sourcePath) {
                writeZos(new File(string), "", zos);
            }
        } finally {
            if (zos != null) {
                zos.close();
            }
        }
        return zipName;
    }

    //写入文件到压缩包
    private void writeZos(File file, String basePath, ZipOutputStream zos) throws IOException {
        if (!file.exists()) {
            throw new FileNotFoundException();
        }
        if (file.isDirectory()) {
            File[] listFiles = file.listFiles();
            if (listFiles.length != 0) {
                for (File childFile : listFiles) {
                    writeZos(childFile, basePath + file.getName() + File.separator, zos);
                }
            }
        } else {
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
            ZipEntry entry = new ZipEntry(basePath + file.getName());
            zos.putNextEntry(entry);
            int count = 0;
            byte data[] = new byte[1024];
            while ((count = bis.read(data)) != -1) {
                zos.write(data, 0, count);
            }
            bis.close();
        }
    }

    /**
     * 删除压缩文件包
     * @param downloadFile
     */
    @Override
    public void deleteDownPackage(File downloadFile) {
        if (downloadFile.getName().endsWith(".zip")) {
            downloadFile.delete();
        }
    }

    @Override
    public List<FileCustom> searchFile(HttpServletRequest request, String currentPath, String reg, String regType) {
        List<FileCustom> list = new ArrayList<>();
        matchFile(request,list,new File(getFilePath(request,currentPath)),reg,regType == null? "" : regType);
        return list;
    }


    private void matchFile(HttpServletRequest request,List<FileCustom> list,File dirFile,String reg,String regType){
        File[] listFiles = dirFile.listFiles();
        if(listFiles != null){
            for (File file: listFiles) {
                if(!file.getName().equals(User.RECYCLE)){
                    if(file.isFile()){
                        String suffixType = FileUtils.getFileType(file);
                        if(suffixType.equals(regType) || (reg != null && file.getName().contains(reg))){
                            FileCustom custom = new FileCustom();
                            custom.setFileName(file.getName());
                            custom.setLastTime(FileUtils.formatTime(file.lastModified()));
                            String parentPath = file.getParent();
                            String prePath = parentPath.substring(parentPath.indexOf(getFilePath(request,null)) + getFilePath(request,null).length());
                            custom.setCurrentPath(File.separator + prePath);
                            if(file.isDirectory()){
                                custom.setFileSize("-");
                            }else{
                                custom.setFileSize(FileUtils.getDataSize(file.length()));
                            }
                            custom.setFileType(FileUtils.getFileType(file));
                            list.add(custom);
                        }
                    }else{
                        matchFile(request,list,file,reg,regType);
                    }
                }

            }
        }
    }

    @Override
    public void respFile(HttpServletResponse response, HttpServletRequest request, String currentPath, String fileName, String type) throws IOException {
        File file = new File(getFilePath(request, currentPath), fileName);
        InputStream inputStream = new FileInputStream(file);
        if ("docum".equals(type)) {
            response.setCharacterEncoding("UTF-8");
            IOUtils.copy(inputStream, response.getWriter(), "UTF-8");
        } else {
            IOUtils.copy(inputStream, response.getOutputStream());
        }
    }

    /**
     * 打开office文档文件
     *
     * @param request
     * @param currentPath
     * @param fileName
     * @return
     * @throws Exception
     */
    @Override
    public String openOffice(HttpServletRequest request, String currentPath, String fileName) throws Exception {
        Office office = officeMapper.selectByPrimaryKey(FileUtils.MD5(new File(getFilePath(request, currentPath), fileName)));
        return office.getOfficeid();
    }

    /**
     * 新建文件夹
     *
     * @param request
     * @param currentPath
     * @param directoryName
     * @return
     */
    @Override
    public boolean addDirectory(HttpServletRequest request, String currentPath, String directoryName) {
        File file = new File(getFilePath(request,currentPath),directoryName);
        return file.mkdir();
    }

    /**
     * 删除文件
     *
     * @param request
     * @param currentPath 当前路径
     * @param directoryName 文件名
     * @throws Exception
     */
    @Override
    public void delDirectory(HttpServletRequest request, String currentPath, String[] directoryName) throws Exception {
        for (String fileName : directoryName) {
            //拼接源文件的地址
            String srcPath = currentPath + File.separator + fileName;
            //根据源文件相对地址拼接 绝对路径
            File src = new File(getFilePath(request, srcPath));//即将删除的文件地址
            File dest = new File(getRecyclePath(request));//回收站目录地址
            // 调用commons.jar包中的moveToDirectory移动文件,移至回收站目录
            org.apache.commons.io.FileUtils.moveToDirectory(src, dest, true);
            //保存本条删除信息
            com.qst.yunpan.entity.File file = new com.qst.yunpan.entity.File();
            file.setFilepath(srcPath);
            User user = (User) request.getSession().getAttribute("user");
            file.setUsername(user.getUsername());
            fileMapperDao.insert(file);
            //fileDao.insertFiles(srcPath, UserUtils.getUsername(request));
        }
        //重新计算文件大小
        reSize(request);
    }

    public String getRecyclePath(HttpServletRequest request){
        return getFilePath(request,User.RECYCLE);
    }

    /**
     * 重命名文件
     *
     * @param request
     * @param currentPath
     * @param srcName
     * @param destName
     * @return
     */
    @Override
    public boolean renameDirectory(HttpServletRequest request, String currentPath, String srcName, String destName) {
    //根据源文件名  获取  源地址
        File file = new File(getFilePath(request, currentPath), srcName);
    //根据源文件名  获取  源地址
        File descFile = new File(getFilePath(request, currentPath), destName);
        return file.renameTo(descFile);//重命名
    }

    @Override
    public SummaryFile summarylistFile(String realPath,int number){
        File file = new File(realPath);
        SummaryFile sf = new SummaryFile();
        List<SummaryFile> returnlist = new ArrayList<SummaryFile>();
        if(file.isDirectory()) {
            sf.setisFile(false);
            if (realPath.length() <= number) {
                sf.setfileName("我的云盘");
                sf.setPath("");
            } else {
                String path = file.getPath();
                sf.setfileName(file.getName());
                //截取固定长度的字符串，从number开 始到value. length-number结束.
                sf.setPath(path.substring(number));
            }
            /*设置抽象文件夹的包含文件集合*/
            for (File filex : file.listFiles()) {
                //获取当 前文件的路径，构造该文件
                SummaryFile innersF = summarylistFile(filex.getPath(), number);
                if (!innersF.getisFile()) {
                    returnlist.add(innersF);
                }
            }
            sf.setListFile(returnlist);
            /*设置抽象文件夹的包含文件夹个数*/
            sf.setListdiretory(returnlist.size());
        }else {
            sf.setisFile(true);
        }
        return sf;
    }

    /**
     * 复制文件夹
     *
     * @param request
     * @param currentPath
     * @param directoryName
     * @param targetdirectorypath
     * @throws Exception
     */
    @Override
    public void copyDirectory(HttpServletRequest request, String currentPath, String[] directoryName, String targetdirectorypath) throws Exception {
        for (String srcName : directoryName) {
            File srcFile = new File(getFilePath(request, currentPath), srcName);
            File targetFile = new File(getFilePath(request, targetdirectorypath), srcName);
            String srcname = srcName;
            String prefixname = "";
            String targetname = "";
            if (targetFile.exists()) {
                String[] srcnamesplit = srcname.split("\\)");
                if (srcnamesplit.length > 1) {//里面有同名的
                    String instring = srcnamesplit[0].substring(1);
                    Pattern pattern = Pattern.compile("[0-9]*");
                    Matcher isNum = pattern.matcher(instring);
                    if (isNum.matches()) {
                        srcname = srcname.substring(srcnamesplit[0].length() + 1);
                    }
                }
                for (int i = 1; true; i++) {
                    prefixname = "(" + i + ")";
                    targetname = prefixname + srcname;
                    targetFile = new File(targetFile.getParent(), targetname);
                    if (!targetFile.exists()) {
                        break;
                    }
                }
                targetFile = new File(targetFile.getParent(), targetname);
            }
            copyfile(srcFile, targetFile);//复制
        }
        //复制文件/文件夹，重新计算大小
        reSize(request);
    }

    /**
     * 复制文件
     *
     * @param srcFile
     * @param targetFile
     * @throws IOException
     */
    @Override
    public void copyfile(File srcFile, File targetFile) throws IOException {
        if(!srcFile.isDirectory()){
            targetFile.createNewFile();
            FileInputStream src = (new FileInputStream(srcFile));
            FileOutputStream target = new FileOutputStream(targetFile);
            FileChannel in = src.getChannel();
            FileChannel out = target.getChannel();
            in.transferTo(0,in.size(),out);
            src.close();
            target.close();
        }else{
            File[] listFiles = srcFile.listFiles();
            targetFile.mkdir();//建立一个子目录
            for(File file : listFiles){
                File realtargetFile = new File(targetFile,file.getName());
                copyfile(file,realtargetFile);
            }
        }
    }

    /**
     * 移动文件或者文件夹
     *
     * @param request
     * @param currentPath
     * @param directoryName
     * @param targetdirectorypath
     * @throws Exception
     */
    @Override
    public void moveDirectory(HttpServletRequest request, String currentPath, String[] directoryName, String targetdirectorypath) throws Exception {
        for (String srcName : directoryName) {
            File srcFile = new File(getFilePath(request, currentPath), srcName);
            File targetFile = new File(getFilePath(request, targetdirectorypath), srcName);
            String srcname = srcName;
            String prefixname = "";
            String targetname = "";
            if (targetFile.exists()) {
                String[] srcnamesplit = srcname.split("\\)");
                if (srcnamesplit.length > 1) {//里面有同名的
                    String instring = srcnamesplit[0].substring(1);
                    Pattern pattern = Pattern.compile("[0-9]*");
                    Matcher isNum = pattern.matcher(instring);
                    if (isNum.matches()) {
                        srcname = srcname.substring(srcnamesplit[0].length() + 1);
                    }
                }
                for (int i = 1; true; i++) {
                    prefixname = "(" + i + ")";
                    targetname = prefixname + srcname;
                    targetFile = new File(targetFile.getParent(), targetname);
                    if (!targetFile.exists()) {
                        break;
                    }
                }
                targetFile = new File(targetFile.getParent(), targetname);
            }
            copyfile(srcFile, targetFile);//复制
            delFile(srcFile);//删除
        }
    }

    //删除文件/文件夹
    private void delFile(File srcFile) throws Exception{
        if (!srcFile.isDirectory()){//文件直接删除
            srcFile.delete();
            return;
        }
        File[] listFile=srcFile.listFiles();
        for (File file : listFile) {
            if(file.isDirectory()){
                delFile(file);
            }else {
                if (file.exists()){
                    file.delete();
                }
            }
        }
        if (srcFile.exists()){
            srcFile.delete();
        }
    }


    @Override
    public List<RecycleFile> recycleFiles(HttpServletRequest request) throws Exception {
        FileExample example = new FileExample();
        User user = (User) request.getSession().getAttribute("user");
        example.createCriteria().andUsernameEqualTo(user.getUsername());
        //通过用户查找用户分享的文件
        List<com.qst.yunpan.entity.File> files = fileMapperDao.selectByExample(example);
        List<RecycleFile> recycleFiles = new ArrayList<RecycleFile>();
        for(com.qst.yunpan.entity.File file : files){
            RecycleFile recycleFile = new RecycleFile();
            File f = new File(getRecyclePath(request),new File(file.getFilepath()).getName());
            recycleFile.setFileId(file.getFileid());
            recycleFile.setFileName(f.getName());
            recycleFile.setFilePath(file.getFilepath());
            if(f.isDirectory()){
                recycleFile.setFileSize("-");
            }else{
                recycleFile.setFileSize(FileUtils.getDataSize(f.length()));
            }
            //将文件最后一次修改的时间注入 （整个项目的分享、删除时间可以说是不合理的，这里写入的时间是文件的时间，而不是被删除时的时刻）
            recycleFile.setLastTime(FileUtils.formatTime(f.lastModified()));
            //将RecycleFile插入到List<RecycleFile>
            recycleFiles.add(recycleFile);
        }
        return recycleFiles;
    }

    @Override
    public void revertDirectory(HttpServletRequest request, int[] fileId) throws Exception {
        System.out.println("还原回收站功能"+request+"   id   "+ fileId.toString());
        for(int id : fileId){
            FileExample example = new FileExample();
            example.createCriteria().andFileidEqualTo(id);
            com.qst.yunpan.entity.File file = fileMapperDao.selectByPrimaryKey(id);
            String fileName = new File(file.getFilepath()).getName();
            File src = new File(getRecyclePath(request),fileName);
            File dest = new File(getFilePath(request,file.getFilepath()));
            org.apache.commons.io.FileUtils.moveToDirectory(src,dest.getParentFile(),true);
            fileMapperDao.deleteByExample(example);
        }
        //当回收站的文件/文件夹还原时，重新计算文件大小
        reSize(request);
    }

    @Override
    public void delAllRecycle(HttpServletRequest request) throws Exception {
        //获取回收站中的所有文件
        File file = new File(getRecyclePath(request));
        //遍历回收站文件夹下的所有文件
        File[] inferiorFile = file.listFiles();
        for(File file1: inferiorFile){
            //调用本类下面的delFile（）方法
            delFile(file1);
        }
//        根据用户进行删除
        User user = (User) request.getSession().getAttribute("user");
        FileExample example = new FileExample();
        example.createCriteria().andUsernameEqualTo(user.getUsername()) ;
        fileMapperDao.deleteByExample(example);
//        reSize(request);
    }


    @Override
    public void delRecycle(HttpServletRequest request,int fileId) throws Exception {//删除回收站单个文件
        com.qst.yunpan.entity.File file = fileMapperDao.selectByPrimaryKey(fileId);
        String fileName = new File(file.getFilepath()).getName();
        File file1 = new File(getRecyclePath(request),fileName);
        delFile(file1);
        fileMapperDao.deleteByPrimaryKey(fileId);
    }
}
